import { EcsWorld } from "./EcsWorld";
import { EcsComponent } from "./EcsComponent";

export class EcsEntity {
	public id: number = 0;
	public world: EcsWorld = null;
	public __componentsID: number = 0;
	public __valid: boolean = false;
	public __comps: EcsComponent[] = [];
	_factory: EntityType = null;
	constructor(factory: EntityType) {
		this._factory = factory;
	}

	public recover() {
		for (let i = 0; i < this.__comps.length; i++) {
			if (this.__comps[i]) {
				this.__comps[i].enable = false;
			}
		}
		this._factory.recover(this);
	}

	public destroy(): boolean {
		//TODO
		return true;
	}

	public get valid(): boolean {
		return this.__valid;
	}

	public get componentsID(): number {
		return this.__componentsID;
	}

	public enable(idOrCompClz: number | (new () => EcsComponent)): EcsComponent {
		let id = typeof idOrCompClz == 'number' ? idOrCompClz : EcsComponent.getIdFromClz(idOrCompClz)
		let _comp: EcsComponent = this.__comps[id];
		if (_comp && !_comp.enable) {
			_comp.enable = true;
			this.__componentsID += Math.pow(2, id);
			this.world.__onEnableComponent(this, id);
		}
		return _comp;
	}

	public disable(idOrCompClz: number | (new () => EcsComponent)): EcsComponent {
		let id = typeof idOrCompClz == 'number' ? idOrCompClz : EcsComponent.getIdFromClz(idOrCompClz)
		let _comp: EcsComponent = this.__comps[id];
		if (_comp && _comp.enable) {
			_comp.enable = false;
			this.__componentsID -= Math.pow(2, id);
			this.world.__onDisableComponent(this, id);
		}
		return _comp;
	}

	public isEnable(idOrCompClz: number | (new () => EcsComponent)): boolean {
		let id = typeof idOrCompClz == 'number' ? idOrCompClz : EcsComponent.getIdFromClz(idOrCompClz)
		return (this.__comps[id]) == null ? false : this.__comps[id].enable;
	}

	public replace(id: number): EcsComponent {
		let _comp: EcsComponent = this.__comps[id];
		if (_comp.enable) {
			//TODO
			this.world.__onReplaceComponent(this, id);
		}
		return _comp;
	}

	public component<T extends EcsComponent>(compClz: new () => T): T {
		return this.__comps[(compClz as any).__compId] as T;
	}
}

export class EntityType {
	public readonly name: string|number = null;
	private _comps: { id: number, type: any, enable: boolean, args: any[] }[] = [];
	private _compID: number = 0;
	private _ctor: Function = null;
	private _pool: EcsEntity[] = [];

	public constructor(name: string|number) {
		this.name = name;
	}

	/**定义组件 */
	public defineComp<T extends EcsComponent>(componentClz: new () => T, enable: boolean = true, ...args: any[]): EntityType {
		const componentId: number = EcsComponent.getIdFromClz(componentClz);
		this._comps.push({ id: componentId, type: componentClz, enable: enable, args: args });
		if (enable) {
			this._compID += Math.pow(2, componentId);
		}
		return this;
	}

	/**定义组件 */
	public defineComps(components: (new () => EcsComponent)[], enable: boolean = true, ...args: any[]): EntityType {
		components.forEach(element => {
			this.defineComp(element, enable, args)
		});

		return this;
	}
	/**定义构造方法 */
	public defineCtor(func: (entiy: EcsEntity, ...args) => void): EntityType {
		this._ctor = func;
		return this;
	}

	public create(world: EcsWorld, id: number, args: any[]): EcsEntity {
		let _obj: EcsEntity = this._pool.length > 0 ? this._pool.pop() : null;
		if (_obj == null) {
			_obj = new EcsEntity(this);
			for (let i = 0; i < this._comps.length; i++) {
				let _def = this._comps[i];
				let _comp = new _def.type(..._def.args);
				_obj.__comps[_def.id] = _comp;
			}
		}
		else {
			// for (let i = 0; i < this._comps.length; i++) {
			// 	let _def = this._comps[i];
			// 	_def.type.call(_obj.__comps[_def.id], ..._def.args);
			// }
		}
		_obj.id = id
		_obj.__componentsID = this._compID;
		_obj.world = world;
		for (let i = 0; i < this._comps.length; i++) {
			let _def = this._comps[i];
			_obj.__comps[_def.id].ownerId = _obj.id;
			_obj.__comps[_def.id].enable = _def.enable;
		}


		if (this._ctor) {
			this._ctor.call(_obj, _obj, ...args);
		}
		_obj.__valid = true;
		return _obj;
	}

	public recover(entity: EcsEntity): void {
		entity.id = 0;
		entity.__valid = false;
		this._pool.push(entity);
	}
}